#!/bin/sh
#-
# Copyright (c) 2010 iXsystems, Inc., All rights reserved.
#   Written by:	Xin LI
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

. /etc/rc.subr


#
#	FreeNAS settings	
#
: ${FREENAS_CONFIG:="/data/freenas-v1.db"}
: ${FREENAS_CONFIG_MD5:="/var/tmp/freenas_config.md5"}
: ${FREENAS_SQLITE_CMD:="/usr/local/bin/sqlite3fn"}
: ${FREENAS_RC:="/var/tmp/rc.conf.freenas"}
: ${FREENAS_VERSIONFILE:="/etc/version"}
: ${FREENAS_SYSTEMDATASET:="/var/db/system"}

#
#	Updater specific variables.
#
: ${CD_UPGRADE_SENTINEL="/data/cd-upgrade"}
: ${NEED_UPDATE_SENTINEL="/data/need-update"}

#
#   Installer specific variables.
#
: ${FIRST_INSTALL_SENTINEL="/data/first-boot"}

#
#	OPENSSL settings
#
: ${OPENSSL:="/usr/local/bin/openssl"}
: ${SSLDIR:="/etc/certificates"}
: ${SSLCADIR:="${SSLDIR}/CA"}

#
#	HTTPD settings
#
: ${HTTPD:="/usr/local/sbin/nginx"}
: ${HTTPDDIR:="/usr/local/etc/nginx"}
: ${HTTPDSCRIPT:="/usr/local/etc/rc.d/nginx"}
: ${HTTPDCONF:="${HTTPDDIR}/nginx.conf"}
: ${HTTPDSSLPORT:="443"}
: ${HTTPDPORT:="80"}

#
#	Misc settings
#
: ${FREENAS_CACHEDIR:="/var/tmp/.cache"}
: ${FREENAS_CACHESIZE:="2g"}
: ${FREENAS_CACHEEXPIRE:="60"}

#
#	LDAP settings
#
: ${SSSD_CONF:="/usr/local/etc/sssd/sssd.conf"}
: ${LDAP_CONF:="/usr/local/etc/openldap/ldap.conf"}
: ${CERT_FILE:="/usr/local/etc/certs/cacert.crt"}
: ${LDAP_TIMEOUT:="0"}

#
#	PAM settings
#
: ${PAM_TEMPLATE_DIR:="/etc/ix/templates/pam.d"}
: ${PAM_DIR:="/etc/pam.d"}

#
#	NSS settings
#
: ${PATH_NS_CONF:="/etc/nsswitch.conf"}

#
#	Kerberos settings
#
: ${PATH_KRB5_TEMPLATE:="/etc/ix/templates/kerberos/krb5.conf"}
: ${PATH_KRB5_CONFIG:="/etc/krb5.conf"}
: ${PATH_AD_KEYTAB:="/etc/AD.keytab"}

#
#	Samba settings
#
: ${SAMBA_CONF:="/usr/local/etc/smb4.conf"}

#
#	Warden settings
#
: ${WARDEN:="/usr/local/bin/warden"}

#
#	TrueNAS
#
: ${HA_MODE_FILE="/tmp/.ha_mode"}


__escape()
{
	local val delim

	val="${1}"
	delim="${2}"

	if [ -n "${val}" -a -z "${delim}" ]
	then
		echo -n "${val}" | sed -Ee 's|\\|\\\\|g' -Ee 's|[^a-zA-Z0-9]|\\&|g'

	elif [ -n "${val}" -a -n "${delim}" ]
	then
		echo -n "${val}" | awk -v delim="${delim}" '{
			newstr = "";

			split(delim, delims, "");
			dlength = length(delims);

			split($0, chars, "");
			clength = length(chars);

			for (i = 1;i <= clength;i++) {
				for (j = 1;j <= dlength;j++) {
					if (chars[i] == delims[j]) {
						newstr = newstr "\\";
						break;
					}
				}
				newstr = newstr chars[i];
			}
			printf("%s", newstr);
		}'
	fi
}

__unescape()
{
	local delim

	val="${1}"
	delim="${2}"

	if [ -n "${val}" -a -z "${delim}" ]
	then
		echo -n "${1}" | sed -Ee 's|\\([^a-zA-Z0-9])|\1|g' -Ee 's|\\\\|\\|g'

	elif [ -n "${val}" -a -n "${delim}" ]
	then
		echo -n "${val}" | awk -v delim="${delim}" '{
			newstr = "";

			split(delim, delims, "");
			dlength = length(delims);

			split($0, chars, "");
			clength = length(chars);

			for (i = 1;i <= clength;i++) {
				for (j = 1;j <= dlength;j++) {
					if (chars[i] == "\\" && i + 1 <= clength &&
						chars[i + 1] == delims[j]) {
						i += 1;
						break;
					}
				}
				newstr = newstr chars[i];
			}
			printf("%s", newstr);
		}'
	fi
}


for dsfile in $(ls /etc/directoryservice/rc.*)
do
	. "${dsfile}"
done


# Create a cache filesystem on ${FREENAS_CACHEDIR} .
#
# Used by ix-activedirectory and ix-ldap.
#
# Returns 0 if successful; 1 if unsuccessful.
#
# NOTE: This function is intentionally noisy to catch potential logic errors
# with the boot process.
#
# TODO: add permanent store capability; using the mdconfig* rc.d scripts would
# be a good idea as it features pre-populating / fsck functionality for memory
# disk images, whereas mdmfs doesn't.
# TODO: employ something similar for ix-collectd, but only after the permanent
# store capability has been added.
create_cache_filesystem()
{
	local dev

	# test to see if /var is an md, if not, presumably it's been moved to
	# a persistant volume, so skip creating a separate in memory
	# filesystem for it.
	mount | awk '$3 == "/var" {print $1}' | grep -qE "/dev/md[[:digit:]]+"
	if [ $? -ne 0 ] ; then
		if [ ! -d "${FREENAS_CACHEDIR}" ]; then
			mkdir -p "${FREENAS_CACHEDIR}"
		fi
		return 0
	fi

	if [ -d "${FREENAS_CACHEDIR}" ]; then
		dev=$(mount | awk -v "dir=$FREENAS_CACHEDIR" \
			'$3 == dir { print $1 }')
		if [ -n "$dev" ]; then
			if ! umount $dev; then
				return 1
			fi
			if ! mdconfig -d -u ${dev} ; then
				return 1
			fi
		fi
	else
		mkdir -p "${FREENAS_CACHEDIR}"
	fi

	mdmfs -i 4096 -b 4096 -f 512 -s ${FREENAS_CACHESIZE} \
	    md "${FREENAS_CACHEDIR}"
	if [ $? -eq 0 ]; then
		return 0
	fi

	return 1
}

#
#	General purpose utility functions.
#

# 'var to SQL Fields'
#
# sh vars don't work with ',', but SQL fields require use them as required separators.
var_to_sf()
{
	echo $* | sed -e 's/ /, /g'
}

# Is a service enabled?
#
# Parameters:
# 1 - service name, e.g. afp, cifs, etc.
#
# Returns:
# 0 - enabled
# 1 - disabled
# 2 - not found
srv_enabled()
{
	enabled=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} \
	    "SELECT srv_enable from services_services where srv_service = '$1'" \
	    2>/dev/null)
	if [ "$enabled" = 1 ]; then
		return 0
	elif [ "$enabled" = 0 ]; then
		return 1
	fi
	return 2
}

dirsrv_enabled()
{
	local dir="${1}"

	if [ -z "${dir}" ]
	then
		return 0
	fi

	case "${dir}" in 
		activedirectory) activedirectory_enabled; return $? ;;
		domaincontroller) domaincontroller_enabled; return $? ;;
		ldap) ldap_enabled; return $? ;;
		nis) nis_enabled; return $? ;;
	esac

	return 2
}

dirsrv_set()
{
	local dir="${1}"
	local enable="${2}"

	if [ -z "${dir}" -o -z "${enable}" ]
	then
		return 0
	fi

	case "${dir}" in 
		activedirectory) activedirectory_set "${enable}"; return $? ;;
		domaincontroller) domaincontroller_set "${enable}"; return $? ;;
		ldap) ldap_enabled "${enable}"; return $? ;;
		nis) nis_enabled "${enable}"; return $? ;;
	esac

	return 2
}

srv_set()
{
	local service="${1}"
	local enable="${2}"

	if [ -z "${service}" -o -z "${enable}" ]
	then
		return 0
	fi

	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
	UPDATE
		services_services
	SET
		srv_enable = ${enable}
	WHERE
		srv_service = '${service}'
	"

	return $?
}


is_freenas()
{

	if cat ${FREENAS_VERSIONFILE} | grep -qi ^freenas
	then
		return 0
	else
		return 1
	fi

}


#
#	Jail functions
#
jail_get_path()
{
	local name="${1}"

	local path=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
	SELECT
		jc_path
	FROM
		jails_jailsconfiguration
	ORDER BY
		-id
	LIMIT 1")

	if [ -n "${name}" ]
	then
		path="${path}/${name}"
	fi

	echo "${path}"
	return 0
}

jail_get_ipv4()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            awk '/^ipv4:/ { print $2 }'|sed -E 's|DHCP:||'
	return $?
}

jail_get_alias_ipv4()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            grep -E '^alias-ipv4:'|cut -f2 -d:|sed -E 's|^ +||'
	return $?
}

jail_get_bridge_ipv4()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}"|awk '/^bridge-ipv4:/ { print $2 }'
	return $?
}

jail_get_alias_bridge_ipv4()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            grep -E '^alias-bridge-ipv4:'|cut -f2 -d:|sed -E 's|^ +||'
	return $?
}

jail_get_defaultrouter_ipv4()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            awk '/^defaultrouter-ipv4:/ { print $2 }'|sed -E 's|DHCP:||'
	return $?
}

jail_get_ipv6()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            awk '/^ipv6:/ { print $2 }'|sed -E 's|AUTOCONF:||'
	return $?
}

jail_get_alias_ipv6()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            grep -E '^alias-ipv6:'|cut -f2 -d:|sed -E 's|^ +||'
	return $?
}

jail_get_bridge_ipv6()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}"|awk '/^bridge-ipv6:/ { print $2 }'
	return $?
}

jail_get_alias_bridge_ipv6()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            grep -E '^alias-bridge-ipv6:'|cut -f2 -d:|sed -E 's|^ +||'
	return $?
}

jail_get_defaultrouter_ipv6()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}" | \
            awk '/^defaultrouter-ipv6:/ { print $2 }'|sed -E 's|AUTOCONF:||'
	return $?
}

jail_get_ip_and_netmask()
{
	local ip="${1}"

	if [ -z "${ip}" ]
	then
		return 1
	fi

	JIP=`echo "${ip}" | cut -f1 -d'/'`
	JMASK=`echo "${ip}" | cut -f2 -d'/' -s`

	return 0
}

jail_get_autostart()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}"|awk '/^autostart:/ { print $2 }'
	return $?
}

jail_get_status()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}"|awk '/^status:/ { print $2 }'
	return $?
}

jail_get_type()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} list -v "${name}"|awk '/^type:/ { print $2 }'
	return $?
}

jail_get_jid()
{
	local name="${1}"
	local jid=""

	if [ -z "${name}" ]
	then
		return 1
	fi

	local jail=$(jls name|awk -v jail="^${name}\$" '$1 ~ jail { print $1 }')
	if [ -n "${jail}" ]
	then
		jls jid name|awk -v jail="^${jail}\$" '$2 ~ jail { print $1 }'
		return $?
	fi

	return  1
}

jail_get()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		${WARDEN} list|tail +3
	else
		${WARDEN} list|tail +3|awk -v name="^${name}\$" '$1 ~ name { print $0 }'
	fi

	return $?
}

jail_start()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} start "${name}"
	return $?
}

jail_stop()
{
	local name="${1}"

	if [ -z "${name}" ]
	then
		return 1
	fi

	${WARDEN} stop "${name}"
	return $?
}

jail_update_fstab()
{
	local jail="${1}"
	local jc_path
	local jail_path
	local jail_metadir

	local IFS="|"
	jc_path=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			jc_path

		FROM
			jails_jailsconfiguration

		ORDER BY
			-id

		LIMIT
			1
	")

	jail_metadir="${jc_path}/.${jail}.meta"
	jail_path="${jc_path}/${jail}"

	: > "${jail_metadir}/fstab"
	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			source,
			destination,
			readonly

		FROM
			jails_jailmountpoint

		WHERE
			jail = '${jail}'

		ORDER BY
			-id
	" | \
	while read -r source destination ro
	do
		local mntopts="rw"
		if [ "${ro}" = "1" ]; then
			mntopts="ro"
		fi
		echo "$(echo ${source} | sed 's/ /\\040/g') $(echo ${jail_path}/${destination} | sed 's/ /\\040/g') nullfs ${mntopts} 0 0" >> "${jail_metadir}/fstab"
	done
}

jail_pre_start()
{
	jail_update_fstab "${1}"
}

jail_post_start()
{
	local jail="${1}"
	local ipv4
	local ipv6
	local jip
	local jid

	ipv4=$(jail_get_ipv4 "${jail}")
	ipv6=$(jail_get_ipv6 "${jail}")
	jid="$(jail_get_jid "${jail}")"

	if [ -n "${ipv4}" ]
	then
		jail_get_ip_and_netmask "${ipv4}"
		jip="${JIP}"

	elif [ -n "${ipv6}" ]
	then
		jail_get_ip_and_netmask "${ipv6}"
		jip="${JIP}"
	fi

	local IFS="|"
	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			plugin_name,
			plugin_path,
			plugin_port

		FROM
			plugins_plugins

		WHERE
			plugin_jail = '${jail}'

		ORDER BY
			-id
	" | \
	while read -r plugin_name plugin_path plugin_port
	do
		local control="${plugin_path}/control"

		# Because /etc/rc is currently running in background
		# lets make sure ldconfig is started before the plugin control
		jexec ${jid} /usr/sbin/service ldconfig start > /dev/null
		jexec ${jid} env LD_LIBRARY_PATH=/usr/local/lib/compat "${control}" start "${jip}" "${plugin_port}"
	done
}

jail_pre_stop()
{
	local jail="${1}"
	local jid

	jid="$(jail_get_jid "${jail}")"

	local IFS="|"
	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			plugin_name,
			plugin_path

		FROM
			plugins_plugins

		WHERE
			plugin_jail = '${jail}'

		ORDER BY
			-id
	" | \
	while read -r plugin_name plugin_path
	do
		local control="${plugin_path}/control"

		echo jexec ${jid} "${control}" stop
		jexec ${jid} "${control}" stop
	done
}

jail_post_stop()
{
	local jail="${1}"
	local jid

	jid="$(jail_get_jid "${jail}")"

	local IFS="|"
	jc_path=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			jc_path

		FROM
			jails_jailsconfiguration

		ORDER BY
			-id

		LIMIT
			1
	")

	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			destination

		FROM
			jails_jailmountpoint

		WHERE
			jail = '${jail}'

		ORDER BY
			-id
	" | \
	while read -r destination
	do
		umount -f "${jc_path}/${jail}/${destination}" >/dev/null 2>&1
	done
}

jail_pre_delete()
{
	local jail="${1}"
	local jc_path
	local jail_metadir

	local IFS="|"
	jc_path=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		SELECT
			jc_path

		FROM
			jails_jailsconfiguration

		ORDER BY
			-id

		LIMIT
			1
	")

	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		DELETE FROM
			jails_jailmountpoint

		WHERE
			jail = '${jail}'
	"

	${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} "
		DELETE FROM
			plugins_plugins

		WHERE
			plugin_jail = '${jail}'
	"
}

jail_post_delete()
{
	:
}

system_dataset_enabled()
{
	test -n "$(find "${FREENAS_SYSTEMDATASET}" -prune -type d ! -empty 2>/dev/null)"
}

ha_mode()
{
	local encstat manual node serial

	if [ -f ${HA_MODE_FILE} ]; then
		cat ${HA_MODE_FILE}
		return 0
	fi

	/usr/local/bin/python /usr/local/www/freenasUI/failover/detect.py 2> /dev/null
	return 0
}

ha_hardware() {

	ha_mode | cut -d ":" -f 1

}

ha_node()
{

	if ! ha_mode | grep -q ":"; then
		return 1
	fi

	ha_mode | cut -d ":" -f 2

	return 0

}

ro_sqlite()
{
	local name ret
	name=$1
	ret=$(mktemp /tmp/${name}.XXXXX 2> /tmp/${name}.rcfail && rm /tmp/${name}.rcfail)
	cp ${FREENAS_CONFIG} ${ret}
	echo ${ret}
}

fncheck_process()
{
	ps -p "$1" > /dev/null 2>&1
	return $?
}

kill_process()
{
	local pid=$1
	local ret=0

	kill ${pid} >/dev/null 2>&1
	ret=$?

	fncheck_process "${pid}"
	if [ "$?" != "0" ]
	then
		kill -9 "${pid}" > /dev/null 2>&1
		ret=$?
	fi

	return ${ret}
}

do_timeout_wait()
{
	local res=0
	local timeout=$1
	shift
	local args="$*"

	${args} &
	pid_args=$!

	trap "kill_process ${pid_args}" SIGINT

	(
		local i=0
		local ret=0

		while [ "${i}" -lt "${timeout}" ]
		do
			fncheck_process "${pid_args}"
			if [ "$?" != "0" ]
			then
				ret=0
				break
			fi

			sleep 1
			$((i+=1)) >/dev/null 2>&1
		done

		fncheck_process "${pid_args}"
		if [ "$?" = "0" ]
		then
			kill_process ${pid_args}
			ret=$?
		fi

		exit ${ret}
	) &
	pid_timeout=$!

	wait ${pid_args}
	args_wait_ret=$?

	wait ${pid_timeout}
	timeout_wait_ret=$?

	if [ "${args_wait_ret}" != 0 ] || [ "${timeout_wait_ret}" != "0" ]
	then
		res=1
	fi

	rm -f "${lf}"
	return ${res}
}

timeout_wait()
{
	local ret=0
	local timeout=$1
	shift
	local args="$*"

	local lf="/tmp/$(echo "${args}"|sha256 2>/dev/null)"

	trap "rm -f ${lf}" 2

	lockf -t 0 -k -s "${lf}" /usr/bin/true
	if [ "$?" != 0 ]
	then
		return 1
	fi

	do_timeout_wait ${timeout} ${args}
	ret=$?

	rm -f "${lf}"
	return ${ret}
}
